package com.mju.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.AntPathMatcher;

import com.mju.dao.RoleUriMapper;
import com.mju.exception.MyAccessDeniedHandler;
import com.mju.model.dto.SecurityUser;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityConfig {
	
	@Autowired
	private JwtAuthFilter jwtAuthFilter;
	
	@Autowired
	private KaptchaFilter kaptchaFilter;
	
	@Autowired
	private AuthenticationFailureHandler failureHandler;
	
	@Autowired
	AuthenticationSuccessHandler authSuccessHandler;
	
	@Autowired
	private RoleUriMapper rolemapper;

	@Bean
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}	

	@Bean
	AuthorizationManager<RequestAuthorizationContext> rbacPermission() {
		return (auth, context)->{
			AntPathMatcher antPathMatcher = new AntPathMatcher();
			boolean hasPermission=false;
			Object principal=auth.get().getPrincipal();	
						
			if(principal instanceof SecurityUser) {				
				SecurityUser sysUser=(SecurityUser) principal;				
				Collection<GrantedAuthority> reachableroles=roleHierarchy().getReachableGrantedAuthorities(sysUser.getAuthorities());							
				//String username=((SecurityUser) principal).getUsername();
				
				List<String> roles=new ArrayList<>();
				for(Object r:reachableroles) {
					roles.add(r.toString().substring(5));//去掉前面的ROLE_
				}
				log.error(roles.toString());
				List<String> uris= rolemapper.selectUriByRole(roles);
				for(String url:uris)
	    		{
	    			if(antPathMatcher.match(url,context.getRequest().getRequestURI()))
	    			{
	    				hasPermission= true;
	    				break;
	    			}
	    		}   		
			}						
			return new AuthorizationDecision(hasPermission);
		};
	}
	
	@Bean
	RoleHierarchyImpl roleHierarchy() {
		return RoleHierarchyImpl.fromHierarchy(
				"ROLE_ADMIN > ROLE_TEACHER\n"
				+ "ROLE_TEACHER > ROLE_STUDENT");
	}
	
	@Bean
	SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		
		http.authorizeHttpRequests(auth -> 
				auth.requestMatchers("/login","/kaptcha").permitAll()
//				.requestMatchers("/users/**","/student/add","/student/delete").hasRole("ADMIN")				
				.anyRequest()
				.access(rbacPermission()))
//				.authenticated())				
				.sessionManagement(m -> m.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
				.formLogin(conf -> {
					conf.successHandler(authSuccessHandler);
					conf.failureHandler(failureHandler);
				})
				.csrf(csrf -> csrf.disable()).cors(cors -> cors.disable())
				.exceptionHandling(ex -> ex.accessDeniedHandler(new MyAccessDeniedHandler())
						.authenticationEntryPoint(new MyAccessDeniedHandler()))
				.addFilterBefore(kaptchaFilter, UsernamePasswordAuthenticationFilter.class)
				.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
		return http.build();
	}
}
